/*
 * author      : 吴中勤 
 * date        : 2018/9/28
 * description : 消息预处理
 */

package com.deepexi.adapter.converter.sap;

import com.deepexi.adapter.facade.moby.MobyConst;
import com.deepexi.adapter.facade.moby.MobyOrderLineSenseEnum;
import com.deepexi.adapter.facade.moby.request.MobyEventBody;
import com.deepexi.adapter.facade.moby.request.MobyOrderLine;
import com.deepexi.adapter.facade.moby.request.MobyOrderLineTreeNode;
import com.deepexi.adapter.sdk.sap.exception.SapBizException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class MobyEventBodyPreProcessor {

    private static final Logger logger = LoggerFactory.getLogger(MobyEventBodyPreProcessor.class);

    @Autowired
    private SapMobyMapService sapMobyMapService;

    /**
     * 特殊业务逻辑处理:
     * 1. 购置税订单行
     * 2. 免费保养包
     */
    public void preProcess(MobyEventBody body) throws SapBizException {
        /* 重要: 处理顺序不要变动, 每个预处理流程会修改 body */
        /* 重要: 处理顺序不要变动, 每个预处理流程会修改 body */
        /* 重要: 处理顺序不要变动, 每个预处理流程会修改 body */
        processFreeMaintain(body);
        processVehiclePurchaseTax(body);

        // 最后处理
        filterEffectiveOrderLine(body);
    }

    /**
     * 处理保养包相关:
     * 将保养包的具体物料拆分出来, 添加一个虚料, 不需要采购
     * <p>
     * 1、删除所有 flatTradeOrderLines 中 maintain / free_maintain 节点
     * 2、处理 outDtoTradeOrderLines 中保养相关的节点, 处理逻辑如下:
     * ... 2.1 找到【包】     -> 变成虚料, 发送到 sap
     * ... 2.2 找到具体物料   -> 记录下来, 保存到 adapter数据库
     * <p>
     * <p>
     * ........【奥迪A4L保养包集合】     --------- outDtoTradeOrderLines
     * ........./             \
     * ......../              \
     * ....1W公里包1  ...   2W公里包N   --------- outDtoTradeOrderLines.lines 这个对应到 虚料
     * .....／   \            /  \
     * ..../     \           /   \
     * ..项目1 项目2换机油  换空滤 换机油 ---------- 价格在这一层进行计算
     * ../  \              /  \
     * ./   \             /   \
     * 备件  工时       备件  工时       ---------- 叶子节点(工时和备件) 进行保存
     */
    private void processFreeMaintain(MobyEventBody body) throws SapBizException {
        if (null == body) return;

        List<MobyOrderLine> list = body.getFlatTradeOrderLineList();
        if (null == list || 0 == list.size()) return;

        /* rawMaintainList. 图示【保养包集合】*/
        List<MobyOrderLineTreeNode> rawMaintainList = getMaintainListFromTreeNode(body.getTreeNodes());

        /* 没有保养相关的内容 */
        if (null == rawMaintainList || 0 == rawMaintainList.size()) {
            return;
        }

        /* 分离出物料包. 图示的 包1  ...   包N  */
        List<MobyOrderLineTreeNode> maintainPackList = new ArrayList<>(8);
        for (MobyOrderLineTreeNode aNode : rawMaintainList) {
            List<MobyOrderLineTreeNode> children = aNode.getChildren();
            if (null != children && 0 != children.size()) {
                maintainPackList.addAll(children);
            }
        }

        /* 需要保留下来的 行项目 */
        List<MobyOrderLine> keepList = new ArrayList<>(16);
        for (MobyOrderLine line : list) {
            if (!sapMobyMapService.isMaintainLine(line)) {
                keepList.add(line);
            }
        }

        /* 对应到虚料的操作 */
        for (MobyOrderLineTreeNode aPackNode : maintainPackList) {
            keepList.add(generateVirtualMaintainLine(aPackNode));
        }
        body.setFlatTradeOrderLineList(keepList);

        /* 从maintainPackList 解析出所有叶子节点 */
        List<MobyOrderLineTreeNode> tmpList = new ArrayList<>(16);
        tmpList.addAll(maintainPackList);
        // List<MobyOrderLineTreeNode> leafList = new ArrayList<>(16);
        while (tmpList.size() > 0) {
            MobyOrderLineTreeNode firstNode = tmpList.remove(0);
            List<MobyOrderLineTreeNode> nodeChildren = firstNode.getChildren();
            if (null == nodeChildren || 0 == nodeChildren.size()) {
                body.addRetainLine(firstNode);
                // leafList.add(firstNode);
            } else {
                tmpList.addAll(nodeChildren);
            }
        }
    }

    /* 保养包 -> 虚料 */
    private MobyOrderLine generateVirtualMaintainLine(MobyOrderLineTreeNode aPackNode) {
        MobyOrderLine virtualMaintainLine = new MobyOrderLine();
        virtualMaintainLine.setTradeOrderId(aPackNode.getTradeOrderId());

        /* 保养包的物料号 需要换成虚拟保养物料号 */
        virtualMaintainLine.setMaterialCode(SapMapConst.VIRTUAL_MATERIAL_4_MAINTAIN);
        virtualMaintainLine.setBundleSkuId(aPackNode.getBundleSkuId());
        // virtualMaintainLine.setBundleSkuId(aPackNode.getMaterialCode());
        //
        virtualMaintainLine.setId(aPackNode.getId());
        virtualMaintainLine.setDescription(aPackNode.getDescription());
        virtualMaintainLine.setProductType(aPackNode.getProductType());
        virtualMaintainLine.setPriceMoney(aPackNode.getPriceMoney());
        virtualMaintainLine.setQuantity(aPackNode.getQuantity());
        virtualMaintainLine.setSyncType(SapMapConst.ORDER_LINE_TYPE_GEN_SYNCED);

        //
        return virtualMaintainLine;
    }

    /**
     * 将购置税订单行, 分离出来, 加到订单行. 购置税需要采购
     * 车辆行 => 车辆行 + 购置税行
     */
    private void processVehiclePurchaseTax(MobyEventBody body) {
        List<MobyOrderLine> list = body.getFlatTradeOrderLineList();
        MobyOrderLine vehicleLine = sapMobyMapService.getVehicleLine(list);
        if (null == vehicleLine) return;

        /* 分离车辆购置税 */
        MobyOrderLine mobyLine = new MobyOrderLine();
        mobyLine.setTradeOrderId(body.getMobyTradeOrder().getId());
        mobyLine.setId(vehicleLine.getPurchaseTaxLineId());
        mobyLine.setMaterialCode(vehicleLine.getPurchaseTaxMaterialCode());
        mobyLine.setQuantity(SapMapConst.QUANTITY_ONE);
        mobyLine.setPriceMoney(vehicleLine.getPurchaseTaxMoney());

        /* 购置税需要采购 */
        mobyLine.setAutoPurchase(MobyConst.AUTO_PURCHASE_YES);
        /* 这个地方注意, 因为从车辆行里面剥离, 商品类型的映射, 没有做到自动映射 */
        mobyLine.setProductType(MobyConst.PRODUCT_TYPE_PURCHASE_TAX);
        /* 生成, 不是原订单行项目 */
        mobyLine.setSyncType(SapMapConst.ORDER_LINE_TYPE_GEN_SYNCED);

        body.addLine(mobyLine);
    }


    /**
     * 输入参数 MobyEventBody 预处理, 检测输入
     * <p>
     * 过滤出【有效】的 订单行, 并作为body的list
     * 1、行项目中, 存在 【有订单号, 物料编码为空】的行, 仅是结构记录。
     * 2、我们仅仅关系有物料编码的订单行, 这个物料会过滤掉
     * 3、如果是 金融、保险, 则需要【该行已确认】后再同步。未确认不同步
     */
    private void filterEffectiveOrderLine(MobyEventBody body) throws SapBizException {
        if (null == body.getFlatTradeOrderLineList())
            throw new SapBizException("moby输入参数校验, 订单行无效 null");

        List<MobyOrderLine> effectiveOrderLine = new ArrayList<>();
        for (MobyOrderLine mobyOrderLine : body.getFlatTradeOrderLineList()) {

            /** 车辆的物料代码 存放在 车辆销售代码里面 */
            if (null == mobyOrderLine.getMaterialCode() && !sapMobyMapService.isVehicleLine(mobyOrderLine)) {
                logger.info("行项目中没有 物料编号, 忽略, {}", mobyOrderLine.toString());
                continue;
            }

            if (sapMobyMapService.isLoanLine(mobyOrderLine) && !mobyOrderLine.isConfirmed()) {
                logger.info("金融【未确认】, 暂不同步到SAP, 忽略, {}", mobyOrderLine.toString());
                continue;
            }

            if (sapMobyMapService.isInsuranceLine(mobyOrderLine) && !mobyOrderLine.isConfirmed()) {
                logger.info("保险【未确认】, 暂不同步到SAP, 忽略, {}", mobyOrderLine.toString());
                continue;
            }

            effectiveOrderLine.add(mobyOrderLine);
        }

        if (0 == effectiveOrderLine.size())
            throw new SapBizException("创建订单, 订单行无效 有物料编码的订单行 size=0");

        body.setFlatTradeOrderLineList(effectiveOrderLine);
    }

    private List<MobyOrderLineTreeNode> getMaintainListFromTreeNode(List<MobyOrderLineTreeNode> treeNodes) {
        if (null == treeNodes || 0 == treeNodes.size()) return null;

        List<MobyOrderLineTreeNode> list = new ArrayList<>();
        for (MobyOrderLineTreeNode aNode : treeNodes) {
            String productType = aNode.getProductType();
            // 为什么不在 MobyOrderLineTreeNode 有个 isMaintain方法呢?
            if (MobyOrderLineSenseEnum.FREE_MAINTAIN_PRODUCT_TYPE.name.equals(productType)
                    || MobyOrderLineSenseEnum.PRODUCT_TYPE_MAINTAIN.equals(productType)) {
                list.add(aNode);
            }
        }
        return list;
    }
}
